use clap::Parser;
use std::{fs, io, path::Path};

#[derive(Default)]
struct WalkConfig {
    max_depth: usize,
    skip_hidden: bool,
    sort_by_name: bool,
    show_icon: bool,
    sort_order: SortOrder,
}

#[derive(clap::ValueEnum, Clone, Debug, Default)]
enum SortOrder {
    #[default]
    FoldersFirst,
    FilesFirst,
}

struct FileNode {
    name: String, // 只存储节点名称
    is_dir: bool,
    children: Vec<FileNode>,
}

impl FileNode {
    fn build(path: &Path, config: &WalkConfig, depth: usize) -> io::Result<Self> {
        let name = extract_name(path);
        let metadata = fs::symlink_metadata(path)?;
        let is_dir = metadata.is_dir();
        let mut children = Vec::new();

        if is_dir && depth < config.max_depth {
            let mut entries: Vec<_> = fs::read_dir(path)?.filter_map(|e| e.ok()).collect();

            // 新增排序逻辑
            let (mut dirs, mut files): (Vec<_>, Vec<_>) = entries
                .into_iter()
                .partition(|e| e.metadata().map(|m| m.is_dir()).unwrap_or(false));

            if config.sort_by_name {
                dirs.sort_by(|a, b| a.file_name().cmp(&b.file_name()));
                files.sort_by(|a, b| a.file_name().cmp(&b.file_name()));
            }

            entries = match config.sort_order {
                SortOrder::FoldersFirst => {
                    dirs.extend(files);
                    dirs
                }
                SortOrder::FilesFirst => {
                    files.extend(dirs);
                    files
                }
            };

            for entry in entries {
                let child_path = entry.path();
                if config.skip_hidden && is_hidden(&child_path) {
                    continue;
                }
                if let Ok(child) = Self::build(&child_path, config, depth + 1) {
                    children.push(child);
                }
            }
        }

        Ok(Self {
            name,
            is_dir,
            children,
        })
    }

    // 打印方法
    fn print(&self, config: &WalkConfig) {
        let is_root = self.name == "."; // 根节点特殊处理
        if is_root {
            for child in &self.children {
                child.print_node("", "", true, config.show_icon);
            }
        } else {
            self.print_node("", "", true, config.show_icon);
        }
    }

    fn print_node(&self, prefix: &str, child_prefix: &str, is_last: bool, show_icon: bool) {
        let icon = if self.is_dir { "📁" } else { "📄" };
        let connector = if is_last { "└─ " } else { "├─ " };

        // 打印当前节点
        if show_icon {
            println!("{}{}{} {}", prefix, connector, icon, self.name);
        } else {
            println!("{}{} {}", prefix, connector, self.name);
        }

        // 处理子节点
        let count = self.children.len();
        for (i, child) in self.children.iter().enumerate() {
            let new_prefix = format!("{}{}", child_prefix, if is_last { "    " } else { "│   " });
            let is_last_child = i == count - 1;

            child.print_node(
                &new_prefix,
                &new_prefix, // 修正前缀传递方式
                is_last_child,
                show_icon,
            );
        }
    }
}

// 跨平台隐藏文件检测
fn is_hidden(path: &Path) -> bool {
    path.file_name()
        .and_then(|n| n.to_str())
        .map(|s| s.starts_with('.'))
        .unwrap_or(false)
}

// 统一名称提取逻辑
fn extract_name(path: &Path) -> String {
    if path == Path::new(".") {
        return ".".to_string();
    }
    path.file_name()
        .and_then(|n| n.to_str())
        .map(|s| s.to_string())
        .unwrap_or_else(|| path.to_string_lossy().replace("\\", "/")) // 统一路径格式
}

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
    /// 最大深度，默认3 (Maximum depth, default 3)
    #[arg(short, long, default_value_t = 3)]
    max_depth: usize,
    /// 显示Icon图标📁和📄，默认不显示 （show the Icon icons 📁 and 📄, not show by default）
    #[arg(long, default_value_t = false)]
    show_icon: bool,
    /// 显示隐藏文件夹，默认true （Show hidden folders, default true）
    #[arg(short, long, default_value_t = true, value_parser, action = clap::ArgAction::Set)]
    skip_hidden: bool,
    /// 根据名称排序
    #[arg(long, default_value_t = false)]
    sort_by_name: bool,
    /// 排序顺序 [possible values: folders-first, files-first]
    #[arg(long, value_enum, default_value_t = SortOrder::FoldersFirst)]
    sort_order: SortOrder,
}

fn main() -> io::Result<()> {
    let args = Args::parse();

    let config = WalkConfig {
        max_depth: args.max_depth,
        skip_hidden: args.skip_hidden,
        show_icon: args.show_icon,
        sort_by_name: args.sort_by_name,
        ..Default::default()
    };

    let root = Path::new(".");
    let tree = FileNode::build(root, &config, 0)?;
    tree.print(&config);
    Ok(())
}
